/* 
 * Copyright (C) 2013, 2014 lex xiang
 *
 * file:	drivers/shell.c
 * history:	2013 Juy 14 created by lex
 * comment:	resolving the command of the user input
 */

#include "shell.h"
#include "command.h"
#include <stdio.h>
#include <string.h>

struct view_s boot_view[] = {
	VIEW_ENTRY(root,  "[boot@tro root]"),
	VIEW_ENTRY(debug, "[boot@tro debug]"),
};

static void shell_do_table(void *arg);
static void shell_do_next(void *arg);
static void shell_do_prev(void *arg);

struct shell g_shell;
struct hist_pool g_hist_pool;

struct shell * shell_alloc()
{
	//return (struct shell *)malloc(sizeof(struct shell));
	return &g_shell;
}

void shell_free(struct shell *sh)
{
	//free(sh);
	return;
}

void shell_init(struct shell *sh, struct tty *tty)
{
	s32 i;
	struct view_s *view;
	struct list_head *pos;
	struct rb_node **p, *parent = NULL;

	sh->name	= "shell";
	sh->argc	= 0;
	sh->tty		= tty;
	sh->curr_view	= &boot_view[0];

	INIT_LIST_HEAD(&sh->view_list);
	memset(sh->argv, 0, sizeof(sh->argv));

	/* add a view list */
	for (i = 0; i < sizeof(boot_view) / sizeof(struct view_s); ++i) {
		INIT_LIST_HEAD(&boot_view[i].link);
		list_add_tail(&boot_view[i].link, &sh->view_list);

		rb_tree_init(&boot_view[i].cmd_tr, rb_find, 
			     rb_insert, rb_erase, rb_cmd_cmp);
	}

	//printf_d("nr cmd# %d\n", sizeof(boot_cmd) / sizeof(struct command_s));

	for (i = 0; i < /*sizeof(&boot_cmd) / sizeof(struct command_s)*/2; ++i) {
		list_for_each(pos, &sh->view_list) {
			view = list_entry(pos, struct view_s, link);

			if (strcmp(boot_cmd[i].curr_vwnm, view->name) == 0)
				break;
		}

		if (pos == &sh->view_list) {	/* unfound the view, it's never ture */
			shell_panic();
			return;
		}

		/* 
		 * insert command's red black node to view's red black tree 
		 * in fact we should pend a mutex
		 */
		if (!*(p = view->cmd_tr.op.find(&view->cmd_tr, parent, boot_cmd[i].name)))
			view->cmd_tr.op.insert(&view->cmd_tr, &boot_cmd[i].node, parent, p);
	}

	sh->tty->op.hook_arg		= (void *)sh;
	sh->tty->op.hook[HOOK_HT]	= shell_do_table;
	sh->tty->op.hook[HOOK_SO]	= (fp_tty_misc)0;
	sh->tty->op.hook[HOOK_DLE]	= (fp_tty_misc)0;

	/*
	 * command line history function
	 */
	if ((sh->hist = &g_hist_pool)) {
		sh->tty->op.hook[HOOK_SO]	= shell_do_next;
		sh->tty->op.hook[HOOK_DLE]	= shell_do_prev;

		INIT_LIST_HEAD(&sh->hist->list);

		for (i = 0; i < NR_HIST_MAX; ++i) {
			INIT_LIST_HEAD(&sh->hist->entry[i].link);
			list_add_tail(&sh->hist->entry[i].link, &sh->hist->list);
		}

		sh->hist->curr_hist	= &sh->hist->entry[0];
		sh->hist->next_hist	= &sh->hist->entry[0];
		sh->hist->prev_hist	= &sh->hist->entry[0];
		sh->hist->latest	= &sh->hist->entry[0];
	}
	/*
	 * command match function
	 */
	sh->nr_match = 0;
	INIT_LIST_HEAD(&sh->match_list);

	/*
	 * now, ready go! 
	 */
	shell_do_parse((void *)sh);
	return ;
}

void shell_do_parse(void *arg)
{
	u8 *head, *tail, *tmp, *cmd_head;
	u32 len, i;
	struct shell *sh  = (struct shell *)arg;
	struct tty *tty = sh->tty;
	struct command_s *cmd;
	struct rb_node **p, *pa;
	struct rb_tree *tree;

	while (1) {
		tty->op.puts(tty, sh->curr_view->prompt);
		tty->op.save_pos(tty, sh->curr_view->prompt);

		if ((len = tty->op.get_line(tty, sh->line, SH_BUF_MAX)) <= 0)
			continue;
		/*
		 * now, parse the input, we should do:
		 * 1. the blank space filtered out
		 * 2. find the command
		 * 3. if command is, set arguments
		 * 4. check out arguments
		 * 5. exceute command
		 */
		for (head = sh->line; *head && (*head == ' ' || *head == '\t'); head++)
			;
		for(tail = sh->line + len; (*tail == ' ' || *tail == '\t') && tail >= head;)
			tail--;
		for (tmp = head; tmp <= tail; tmp++) {
			if (*tmp == 32 || *tmp == '\t')
				*tmp = '\0';
		}

		if (!*head) continue;

		tree = &sh->curr_view->cmd_tr;
		if (!*(p = tree->op.find(tree, pa, head))) {
			tty->op.dev_printf(tty, "command: %s is not found!\n", head);
			continue;
		} else {
			cmd = rb_entry(*p, struct command_s, node);
		}

		tmp = head + strlen(head);
		for (sh->argc = 0; sh->argc < NR_ARGC_MAX; ++sh->argc) {
			while(*tmp == '\0' && tmp < tail)
				tmp++;
			if (tmp >= tail) break;

			strcpy(sh->argv[sh->argc], tmp);
			tmp += strlen(tmp);
		}

		/*
		 * in the further we should set a function which 
		 * check out arguments of the command.
		 */

		if (cmd) {
			cmd->cmd(cmd, sh);
		}
	}

	return;		/* never be return */
}

void shell_do_table(void *arg)
{
	struct shell *sh = (struct shell *)arg;
	struct tty *tty  = sh->tty;
	struct rb_node **p, *pa, *node;
	struct rb_tree *tree = &sh->curr_view->cmd_tr;
	struct command_s *cmd;
	struct list_head *pos;
	u8 *head, *tail, *tmp;
	s8 *_tmp, s[32];
	s32 len, ret, i;

	sh->nr_match = 0;
	INIT_LIST_HEAD(&sh->match_list);

	len = tty->op.read_line(tty, sh->line, SH_BUF_MAX);

	for (head = sh->line; *head && *head == ' '; head++)
		;
	tail = sh->line + len;
	for(tmp = head; tmp <= tail; tmp++) {
		if (*tmp == ' ')
			*tmp = '\0';
	}

	if (!(len = strlen(head))) {
		node = tree->first;

		while (node) {
			cmd = rb_entry(node, struct command_s, node);

			sh->nr_match++;
			list_add_tail(&cmd->link, &sh->match_list);
			node = rb_next(node);
		}

		goto show;
	}

	node = tree->root;
	while (node) {
		cmd = rb_entry(node, struct command_s, node);

		ret = strncmp(cmd->name, head, len);

		if (ret > 0) {
			node = rb_prev(node);
		} else if (ret < 0) {
			node = rb_next(node);
		} else {
			sh->nr_match++;
			list_add_tail(&cmd->link, &sh->match_list);
			node = rb_next(node);
		}
	}

	if (!sh->nr_match)
		return;

	if (sh->nr_match == 1) {
		if (!(cmd = list_entry(sh->match_list.next, struct command_s, link)))
			return;

		if (len == strlen(cmd->name))
			goto match;

		tty->op.insert_str(tty, cmd->name + len);
		return;
	}
show:
	i = 0;
	tty->op.dev_printf(tty, "\n");
	list_for_each(pos, &sh->match_list) {
		cmd = list_entry(pos, struct command_s, link);

		if (!(++i & 0x3))
			tty->op.dev_printf(tty, "\n");

		tty->op.dev_printf(tty, "%-20s", cmd->name);
	}
	tty->op.dev_printf(tty, "\n");
	tty->op.puts(tty, sh->curr_view->prompt);
	return;
match:
	tty->op.dev_printf(tty, "\n%s\n", cmd->cmd_desc->usage);
	tty->prev = 0;
	tty->modified = 1;
	return;
}

void shell_do_next(void *arg)
{

}

void shell_do_prev(void *arg)
{

}

s32 rb_cmd_cmp(struct rb_node *rb_node, void *key)
{
	struct command_s *cmd;

	cmd = rb_entry(rb_node, struct command_s, node);

	return strcmp(cmd->name, (char *)key);
}
